package user

import (
	"context"
	"fmt"
	"time"

	"mall/common/globalkey"
	"mall/common/cryptx"
	"mall/service/backend/common/errorx"
	"mall/service/backend/common/i18n"
	"mall/service/backend/common/msg/logmsg"
	// "mall/service/backend/common/utils/encrypt"
	"mall/service/backend/model"
	"mall/service/backend/rpc/internal/svc"
	"mall/service/backend/rpc/internal/utils/errorhandler"
	"mall/service/backend/rpc/types/backend"

	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type UpdateUserLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

func NewUpdateUserLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateUserLogic {
	return &UpdateUserLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

// group: user
func (l *UpdateUserLogic) UpdateUser(in *backend.UserInfo) (*backend.BaseResp, error) {
	if in.Id == nil || *in.Id == 0 {
		return nil, errorx.NewInvalidArgumentError(i18n.ValidationError)
	}

	var id uint64 = *in.Id

	user, err := l.svcCtx.SysUser.FindOneNoCache(l.ctx, nil, id)
	if err != nil {
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	if in.NewPassword != nil { // 用户修改密码，需要先验证原密码
		password := cryptx.EncryptPasswordWithSalt(*in.Password, user.Salt)
		if password != user.Password {
			return nil, errorx.NewInvalidArgumentError("login.wrongPassword")
		}

		user.Password = cryptx.EncryptPasswordWithSalt(*in.NewPassword, user.Salt)
	} else if in.Password != nil { // 管理员重置密码
		user.Password = cryptx.EncryptPasswordWithSalt(*in.Password, user.Salt)
	}

	if in.Username != nil {
		user.Username = *in.Username

		filterMap := make(map[string]map[string][]any)
		filterMap[model.SysUserColumns.Username] = map[string][]any{
			"equalTo": []any{*in.Username},
		}
		filterMap[model.SysUserColumns.Status] = map[string][]any{
			"equalTo": []any{globalkey.StatusEnabled},
		}
		list, _, err := l.svcCtx.SysUser.GetList(l.ctx, model.M{
			"pageSize": uint64(2),
			"select":   ",",
		}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		count := len(list)
		if count > 1 {
			return nil, errorx.NewAlreadyExistsError("user.usernameAlreadyExists")
		}

		if count == 1 && list[0].ID != id {
			return nil, errorx.NewAlreadyExistsError("user.usernameAlreadyExists")
		}
	}

	sysUserRel := &model.SysUserRel{}
	if len(in.RoleIds) > 0 {
		relFilterMap := make(map[string]map[string][]any)
		relFilterMap[model.SysUserRoleRelationshipColumns.UserID] = map[string][]any{
			"equalTo": []any{id},
		}
		rel, _, err := l.svcCtx.SysUser.GetRelRoleList(l.ctx, model.M{}, -1, relFilterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(rel) > 0 {
			var rids []uint64
			oridMap := make(map[uint64]struct{})
			for _, v := range rel {
				rids = append(rids, v.RoleID)
				oridMap[v.RoleID] = struct{}{}
			}

			nridMap := make(map[uint64]struct{})
			for _, rid := range in.RoleIds {
				nridMap[rid] = struct{}{}
				if _, ok := oridMap[rid]; !ok { // 增RoleId
					sysUserRel.RoleIds = append(sysUserRel.RoleIds, rid)
				}
			}

			for _, rid := range rids {
				if _, ok := nridMap[rid]; !ok { // 删RoleId
					sysUserRel.DelRoleIds = append(sysUserRel.DelRoleIds, rid)
				}
			}
		} else {
			for _, rid := range in.RoleIds {
				sysUserRel.RoleIds = append(sysUserRel.RoleIds, rid)
			}
		}
	}

	if len(in.PositionIds) > 0 {
		relFilterMap := make(map[string]map[string][]any)
		relFilterMap[model.SysUserPositionRelationshipColumns.UserID] = map[string][]any{
			"equalTo": []any{id},
		}
		rel, _, err := l.svcCtx.SysUser.GetRelPositionList(l.ctx, model.M{}, -1, relFilterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if len(rel) > 0 {
			var pids []uint64
			opidMap := make(map[uint64]struct{})
			for _, v := range rel {
				pids = append(pids, v.PositionID)
				opidMap[v.PositionID] = struct{}{}
			}

			npidMap := make(map[uint64]struct{})
			for _, pid := range in.PositionIds {
				npidMap[pid] = struct{}{}
				if _, ok := opidMap[pid]; !ok { // 增PositionId
					sysUserRel.PositionIds = append(sysUserRel.PositionIds, pid)
				}
			}

			for _, pid := range pids {
				if _, ok := npidMap[pid]; !ok { // 删PositionId
					sysUserRel.DelPositionIds = append(sysUserRel.DelPositionIds, pid)
				}
			}
		} else {
			for _, pid := range in.PositionIds {
				sysUserRel.PositionIds = append(sysUserRel.PositionIds, pid)
			}
		}
	}

	if in.Nickname != nil {
		user.Nickname = *in.Nickname
	}
	if in.Email != nil && len(*in.Email) > 0 {
		user.Email = *in.Email

		filterMap := make(map[string]map[string][]any)
		filterMap[model.SysUserColumns.Email] = map[string][]any{
			"equalTo": []any{*in.Email},
		}
		filterMap[model.SysUserColumns.Status] = map[string][]any{
			"equalTo": []any{globalkey.StatusEnabled},
		}
		list, _, err := l.svcCtx.SysUser.GetList(l.ctx, model.M{
			"pageSize": uint64(2),
			"select":   ",",
		}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		count := len(list)
		if count > 1 {
			return nil, errorx.NewAlreadyExistsError("user.emailAlreadyExists")
		}

		if count == 1 && list[0].ID != id {
			return nil, errorx.NewAlreadyExistsError("user.emailAlreadyExists")
		}
	}
	if in.Mobile != nil && len(*in.Mobile) > 0 {
		user.Mobile = *in.Mobile

		filterMap := make(map[string]map[string][]any)
		filterMap[model.SysUserColumns.Mobile] = map[string][]any{
			"equalTo": []any{*in.Mobile},
		}
		filterMap[model.SysUserColumns.Status] = map[string][]any{
			"equalTo": []any{globalkey.StatusEnabled},
		}
		list, _, err := l.svcCtx.SysUser.GetList(l.ctx, model.M{
			"pageSize": uint64(2),
			"select":   ",",
		}, -1, filterMap)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		count := len(list)
		if count > 1 {
			return nil, errorx.NewAlreadyExistsError("user.mobileAlreadyExists")
		}

		if count == 1 && list[0].ID != id {
			return nil, errorx.NewAlreadyExistsError("user.mobileAlreadyExists")
		}
	}
	if in.Avatar != nil {
		user.Avatar = *in.Avatar
	}
	if in.HomePath != nil {
		user.HomePath = *in.HomePath
	}
	if in.Description != nil {
		user.Description = *in.Description
	}
	if in.DepartmentId != nil {
		user.DepartmentID = *in.DepartmentId
	}
	if in.Status != nil {
		user.Status = uint8(*in.Status)
	}
	user.UpdatedAt = time.Now()

	tx, err := l.svcCtx.DB.BeginTx(l.ctx, nil)
	if err != nil {
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	err = l.svcCtx.SysUser.Update(l.ctx, tx, user, sysUserRel)
	if err != nil {
		tx.Rollback()
		return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
	}

	if in.Password != nil || in.RoleIds != nil || in.PositionIds != nil {
		filterMap := make(map[string]map[string][]any)
		filterMap[model.SysTokenColumns.UUID] = map[string][]any{
			"equalTo": []any{*in.Id},
		}
		err = l.svcCtx.SysToken.UpdateBatch(l.ctx, tx, []uint64{}, model.M{
			model.SysTokenColumns.Status: uint8(globalkey.StatusDisabled),
		}, filterMap)
		if err != nil {
			logrus.Info(fmt.Sprintf("UpdateUser BlockUserAllToken err: %+v", err))
		}
		if err == nil || err == model.ErrNotUpdated {
			filterMap[model.SysTokenColumns.Status] = map[string][]any{
				"equalTo": []any{uint8(globalkey.StatusDisabled)},
			}
			res, _, err := l.svcCtx.SysToken.GetList(l.ctx, model.M{}, -1, filterMap)
			if err != nil {
				logrus.Info(fmt.Sprintf("UpdateUser BlockUserAllToken GetList err: %+v", err))
				// tx.Rollback()
				// return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
			}
			if err == nil && len(res) > 0 {
				nowTime := time.Now().Unix()
				for _, v := range res {
					if v.ExpiredAt.Valid {
						expiredTime := int(v.ExpiredAt.Time.Unix() - nowTime)
						if expiredTime > 0 {
							err = l.svcCtx.BizRedis.Setex("token_"+v.Token, "1", expiredTime)
							if err != nil {
								logx.Errorw(logmsg.RedisError, logx.Field("detail", err.Error()))
								return nil, errorx.NewInternalError(i18n.RedisError)
							}
						}
					}
				}
			}
		} else {
			tx.Rollback()
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}
	}

	tx.Commit()

	return &backend.BaseResp{
		Msg: i18n.UpdateSuccess,
	}, nil
}
